﻿using System;
using System.IO;
using System.Threading;

namespace CommonLibraries.DeviceLibrary
{
    public enum ReceptionMode
    {
        AMode,
        BMode,
        None
    }

    public class SerialErrorReceivedException : ApplicationException
    {
        public SerialErrorReceivedException(string message)
            : base(message)
        {
        }
    }

    public class SerialPortMonitor:IDisposable
    {
        public enum GrilMessages
        {
            None,
            StartGPGGA,
            StopGPGGA,
            StopAll
        }
        private System.IO.Ports.SerialPort prop_SerialPort = null;
        private Timer timer;

        private System.IO.Ports.SerialPort SerialPort
        {
            get
            {
                if (prop_SerialPort == null)
                {
                    SerialPort = new System.IO.Ports.SerialPort();
                    //SerialPort.DataReceived += ReceiveMessage;
                    SerialPort.ErrorReceived += ReceiveError;
                    SerialPort.PortName = "COM2";
                    SerialPort.BaudRate = 115200;
                }
                return prop_SerialPort;
            }
            set
            {
                prop_SerialPort = value;
            }
        }

        public void ReadMessage(object o)
        {
            ProcessReceivedMessage();
        }

        public void Open()
        {
            if (IsOpen)
                return;

            SerialPort.Open();
            ChangeToMode(ReceptionMode.BMode);
        }

        public void ChangeToMode(ReceptionMode newMode)
        {
            switch (newMode)
            {
                case ReceptionMode.AMode:
                    SerialPort.DataReceived -= ReceiveMessage;

                    if (timer != null)
                        timer.Dispose();

                    timer = new Timer(ReadMessage, new AutoResetEvent(false), 0, 150);

                    break;
                case ReceptionMode.BMode:
                    SerialPort.DataReceived += ReceiveMessage;

                    if (timer != null)
                        timer.Dispose();

                    break;
            }            
        }
        
        public void Close()
        {
            if (!IsOpen)
                return;

            SerialPort.Close();
        }

        public void WriteLine(GrilMessages grilMessage)
        {
            switch (grilMessage)
            {
                case GrilMessages.None:
                    break;
                case GrilMessages.StartGPGGA:
                    WriteLine("em,,/msg/nmea/GGA:0.5");
                    break;
                case GrilMessages.StopGPGGA:
                    WriteLine("dm,,/msg/nmea/GGA:0.5");
                    break;
                case GrilMessages.StopAll:
                    WriteLine("dm");
                    break;
                default:
                    break;
            }
        }

        public void WriteLine(string sendString)
        {
            if (IsOpen)
                SerialPort.WriteLine(sendString);
        }

        public bool IsOpen
        {
            get
            {
                return (SerialPort != null ? SerialPort.IsOpen : false);
            }
        }

        #region ReceiveError

        void ReceiveError(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
        {
            CreateDebug("ErrorAlRecibir");
            // todo: figure out what this could be
            //throw new SerialErrorReceivedException(e.ToString());
        }



        #endregion

        #region ReceiveMessage
        string _TempMSG = "";
        
        void ReceiveMessage(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            ProcessReceivedMessage();
        }

        private void ProcessReceivedMessage()
        {
            // extract message
            string _Sentence = SerialPort.ReadExisting();

            //StreamReader reader = File.OpenText(".\\posiciones.txt");
            //_Sentence = reader.ReadToEnd();
            //reader.Close();
            //if (!NmeaUtility.IsValidSentence(_Sentence))
            //    return;

            //CreateDebug("GPS->Sentence->"+ _Sentence);
            //_Sentence = "$GPGGA,002454,3553.5295,N,13938.6570,E,1,05,2.2,18.3,M,39.0,M,,*7F";
            //ErrorManager.CreateDebug("Log.txt", "SerialPortMonitor.ReceiveMessage -> " + _Sentence);
            // validate length
            //Thread.Sleep(100);

            if (string.IsNullOrEmpty(_Sentence))
                return;

            // raise generic event

            ProjectCommon.EventArgs<string> _EventArgs = new ProjectCommon.EventArgs<string>(_Sentence);

            if (ReceivedSentence != null)
                ReceivedSentence(this, _EventArgs);

            if (_EventArgs.Cancel)
                return;

            // raise specific event
            if (NmeaUtility.IsValidSentence(_Sentence))
            {
                //CreateDebug("Sentencia->Ejecuta Directo->" + _Sentence);
                try
                {
                    NmeaGGA _NmeaGGA = new NmeaGGA(_Sentence);

                    if (ReceivedNmeaGGA != null)
                        ReceivedNmeaGGA(this, new ProjectCommon.EventArgs<NmeaGGA>(_NmeaGGA));
                }
                catch (Exception ex)
                {
                    CreateDebug("Sentencia->Error->" + ex.Message);
                }

                //if (_NmeaGGA.Quality == 0)
                //    _TempMSG = _Sentence;
                //else
                //{
                //    _TempMSG = "";
                //    if (ReceivedNmeaGGA != null)
                //        ReceivedNmeaGGA(this, new CommonLibraries.ProjectCommon.EventArgs<NmeaGGA>(_NmeaGGA));
                //}
            }
            else
            {
                if (!_Sentence.StartsWith("$GPGGA"))
                {
                    //CreateDebug("Sentencia->Termina->" + _Sentence);
                    _Sentence = _TempMSG + _Sentence;

                    if (NmeaUtility.IsValidSentence(_Sentence))
                    {
                        // CreateDebug("Sentencia->Ejecuta partido->" + _Sentence);
                        try
                        {
                            NmeaGGA _NmeaGGA = new NmeaGGA(_Sentence);
                            //if(_NmeaGGA.Quality > 0)
                            //    CreateDebug("Sentencia->Ejecuta partido Exitosamente->" + _Sentence);
                            if (ReceivedNmeaGGA != null)
                                ReceivedNmeaGGA(this, new ProjectCommon.EventArgs<NmeaGGA>(_NmeaGGA));
                        }
                        catch (Exception ex)
                        {
                            CreateDebug("Sentencia->Error->" + ex.Message);
                        }
                    }
                }
                else
                {
                    //CreateDebug("Sentencia->Empieza->" + _Sentence);
                    _TempMSG = _Sentence;
                }
            }

            //try
            //{
            //     NmeaGGA _NmeaGGA;
            //    _NmeaGGA = new NmeaGGA(_Sentence);
            //    if (ReceivedNmeaGGA != null)
            //        ReceivedNmeaGGA(this, new CommonLibraries.ProjectCommon.EventArgs<NmeaGGA>(_NmeaGGA));
            //}
            //catch(Exception ex)
            //{
            //    CreateDebug("Error al Convertir NmeaGGA->" + ex.Message);
            //}
            //sentence.StartsWith("$GPGGA") && !(sentence.Substring(sentence.Length - 3, 1) == "*")            
        }

        public event EventHandler<ProjectCommon.EventArgs<string>> ReceivedSentence;
        public event EventHandler<ProjectCommon.EventArgs<NmeaGGA>> ReceivedNmeaGGA;


        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            WriteLine(GrilMessages.StopAll);
            if (timer != null)
                timer.Dispose();
            Close();
        }

        #endregion

        protected void CreateDebug(string debugData)
        {
            StreamWriter _a = File.AppendText(".\\DebugClasses.txt");
            _a.WriteLine("##############################");
            _a.WriteLine(System.DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString());
            _a.WriteLine("##############################");
            _a.WriteLine(debugData);
            _a.Close();
            _a.Dispose();

        }

    }
}
